package com.gitee.qdbp.jdbc.plugins.impl;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import com.gitee.qdbp.able.jdbc.ordering.OrderType;
import com.gitee.qdbp.able.jdbc.ordering.Ordering;
import com.gitee.qdbp.jdbc.api.SqlBoot;
import com.gitee.qdbp.jdbc.exception.UnsupportedFieldException;
import com.gitee.qdbp.jdbc.model.FunctionOrdering;
import com.gitee.qdbp.jdbc.plugins.ColumnNameResolver;
import com.gitee.qdbp.jdbc.plugins.DbPluginHelper;
import com.gitee.qdbp.jdbc.plugins.OrderBySqlBuilder;
import com.gitee.qdbp.jdbc.plugins.SqlDialect;
import com.gitee.qdbp.jdbc.sql.SqlBuffer;
import com.gitee.qdbp.jdbc.sql.SqlBuilder;
import com.gitee.qdbp.tools.parse.StringParser;
import com.gitee.qdbp.tools.utils.NamingTools;
import com.gitee.qdbp.tools.utils.ReflectTools;
import com.gitee.qdbp.tools.utils.StringTools;
import com.gitee.qdbp.tools.utils.VerifyTools;

/**
 * OrderBy 函数SQL生成处理类
 *
 * @author zhaohuihua
 * @version 20210510
 */
public class OrderByFunctionSqlBuilder implements OrderBySqlBuilder<FunctionOrdering>, DbPluginHelper.Aware {

    /** 插件容器 **/
    protected DbPluginHelper plugins;

    @Override
    public Class<FunctionOrdering> supported() {
        return FunctionOrdering.class;
    }

    @Override
    public SqlBuffer buildSql(FunctionOrdering ordering, ColumnNameResolver columnResolver, SqlBoot sqlBoot)
            throws UnsupportedFieldException {
        if (ordering.isEmpty()) {
            return sqlBoot.newSqlBuffer(); // 没有指定任何隔离字段, 没法加限制条件
        }
        if (VerifyTools.isNotBlank(ordering.getFunctionName())) {
            return buildFunctionSql(ordering, columnResolver, sqlBoot);
        } else {
            String columnName = columnResolver.getColumnName(ordering.getOrderBy());
            SqlBuilder buffer = sqlBoot.newSqlBuilder();
            buffer.ad(columnName);
            OrderType orderType = ordering.getOrderType();
            if (orderType == OrderType.ASC) {
                buffer.ad("ASC");
            } else if (orderType == OrderType.DESC) {
                buffer.ad("DESC");
            }
            return buffer.out();
        }
    }

    private final Map<String, Method> methodMaps = new HashMap<>();

    protected Method getFunctionHandlerMethod(String methodName) {
        String camelName = NamingTools.toCamelString(methodName);
        if (methodMaps.containsKey(camelName)) {
            return methodMaps.get(camelName);
        }
        Class<?>[] types = new Class<?>[] { Ordering.class, ColumnNameResolver.class, SqlBoot.class };
        Method method = ReflectTools.findMethod(this.getClass(), camelName, types);
        if (method == null) {
            String fmt = "OrderBy function [%s] not found for %s.%s(Ordering, ColumnNameResolver, SqlDialect)";
            String msg = String.format(fmt, methodName, this.getClass().getSimpleName(), camelName);
            throw new IllegalArgumentException(msg);
        }

        Class<?> result = method.getReturnType();
        if (result != SqlBuffer.class) {
            String fmt = "OrderBy function [%s] method %s.%s(Ordering, ColumnNameResolver, SqlDialect) "
                    + "must be return SqlBuffer";
            String msg = String.format(fmt, methodName, this.getClass().getSimpleName(), camelName);
            throw new IllegalArgumentException(msg);
        }

        methodMaps.put(camelName, method);
        return method;
    }

    protected SqlBuffer buildFunctionSql(FunctionOrdering ordering, ColumnNameResolver columnResolver,
            SqlBoot sqlBoot) {
        if ("pinyin".equalsIgnoreCase(ordering.getOrderBy())) {
            // 兼容旧版本 userName(pinyin), 新版本应该是 pinyin(userName)
            if (ordering.getFunctionParams().size() == 0) {
                if (!StringTools.isWordString(ordering.getFunctionName())) {
                    // u.userName(pinyin) // functionName都不是单词, 说明确实是旧版本写法
                    ordering.setOrderBy(ordering.getFunctionName());
                    ordering.setFunctionName("pinyin");
                } else {
                    Class<?>[] types = new Class<?>[] { Ordering.class, ColumnNameResolver.class, SqlDialect.class };
                    String camelName = NamingTools.toCamelString(ordering.getFunctionName());
                    Method method = ReflectTools.findMethod(this.getClass(), camelName, types);
                    if (method == null) { // functionName找不到方法, 说明确实是旧版本写法
                        ordering.setOrderBy(ordering.getFunctionName());
                        ordering.setFunctionName("pinyin");
                    }
                }
            }
        }
        String functionName = ordering.getFunctionName();
        Method method = getFunctionHandlerMethod(functionName);
        return ReflectTools.invokeMethod(this, method, ordering, columnResolver, sqlBoot);
    }

    /**
     * 拼音函数, 使用方法: Orderings.of("pinyin(userName) asc")<br>
     * Oracle: ORDER BY USER_NAME ASC<br>
     * MySQL : ORDER BY CONVERT(USER_NAME USING GBK) ASC
     * 
     * @param ordering 排序对象
     * @param columnResolver 列名转换对象
     * @param sqlBoot SQL启动类
     * @return 排序SQL语句
     */
    public SqlBuffer pinyin(Ordering ordering, ColumnNameResolver columnResolver, SqlBoot sqlBoot) {
        if (VerifyTools.isBlank(ordering.getOrderBy())) {
            String fmt = "OrderBy function [%s] format error, missing 'orderBy'. %s";
            String msg = String.format(fmt, ordering.getFunctionName(), ordering.getOriginal());
            throw new IllegalArgumentException(msg);
        }
        if (ordering.getFunctionParams().size() != 0) {
            String fmt = "OrderBy function [%s] format error, too many params. %s";
            String msg = String.format(fmt, ordering.getFunctionName(), ordering.getOriginal());
            throw new IllegalArgumentException(msg);
        }
        String columnName = columnResolver.getColumnName(ordering.getOrderBy());
        SqlBuilder buffer = sqlBoot.newSqlBuilder();
        buffer.ad(sqlBoot.sqlDialect().toPinyinOrderByExpression(columnName));
        OrderType orderType = ordering.getOrderType();
        if (orderType == OrderType.ASC) {
            buffer.ad("ASC");
        } else if (orderType == OrderType.DESC) {
            buffer.ad("DESC");
        }
        return buffer.out();
    }

    /**
     * include函数, 使用方法: Orderings.of("include(u.userType,'GlobalDecode.userType') asc")<br>
     * 
     * @param ordering 排序对象
     * @param columnResolver 列名转换对象
     * @param sqlBoot SQL启动类
     * @return 排序SQL语句
     */
    public SqlBuffer include(Ordering ordering, ColumnNameResolver columnResolver, SqlBoot sqlBoot) {
        if (VerifyTools.isBlank(ordering.getOrderBy())) {
            String fmt = "OrderBy function [%s] format error, missing 'orderBy'. %s";
            String msg = String.format(fmt, ordering.getFunctionName(), ordering.getOriginal());
            throw new IllegalArgumentException(msg);
        }
        String stringParam = ordering.getOnlyStringParam();
        // 'a.b'最少也得5个字符
        if (VerifyTools.isBlank(stringParam) || stringParam.length() < 5) {
            String fmt = "OrderBy function [%s] format error, params value error. %s";
            String msg = String.format(fmt, ordering.getFunctionName(), ordering.getOriginal());
            throw new IllegalArgumentException(msg);
        }
        String sqlId = StringParser.unwrapQuotationMark(stringParam);
        String columnName = columnResolver.getColumnName(ordering.getOrderBy());
        Map<String, Object> params = new HashMap<>();
        params.put("columnName", columnName);
        return sqlBoot.sqlContainer().generate(sqlId, params);
    }

    /** 插件容器 **/
    @Override
    public void setPlugins(DbPluginHelper plugins) {
        this.plugins = plugins;
    }
}
